package top.hcode.hoj.tyedu;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.nutz.http.Header;
import org.nutz.http.Http;
import org.nutz.http.Response;
import top.hcode.hoj.pojo.entity.org.TyOrg;
import top.hcode.hoj.tyedu.dto.*;

import lombok.Setter;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.springframework.stereotype.Component;
import top.hcode.hoj.utils.crypt.EncryptionUtils;

import java.util.*;

/**
 * 天喻网关客户端
 * 文档地址：https://www.showdoc.com.cn/2026844702572217/9158749877441351
 *
 * @author orangej
 * @since 2024/9/26
 */
@Slf4j
@Component
public class TyGatewayClient {
    @Setter
    private String appId = "D0035E76AE1D7ABE6A5A5E125D555EC1";
    @Setter
    private String appKey = "191b23c817dc4882b01a8c235b48650d";
    @Setter
    private String baseUrl = "https://api.wuhaneduyun.cn";
    private String platformCode = "420100";

    private String accessToken;
    private long lastAccessTokenTime = 0;

    // 统一认证相关参数
    @Setter
    private String uniAppId = "981944177701683200";
    @Setter
    private String uniAppSecret = "SDSbZ/nL2MvPSDRG85HbnfEqJYVLlyNoT5UaCXOtSdg=";
    @Setter
    private String uniAppCode = "bd9b7da5946448858fb2f8a58c7d03e1";

    public String getAccessToken() throws RuntimeException {
        if (accessToken == null || System.currentTimeMillis() - lastAccessTokenTime > 30 * 60 * 1000) {
            accessToken = buildAccessToken();
            lastAccessTokenTime = System.currentTimeMillis();
        }

        return accessToken;
    }

    public String buildAccessToken() {
        Map<String, String> params = new HashMap<>();
        String timestamp = String.valueOf(System.currentTimeMillis());
        params.put("appId", appId);
        params.put("timeStamp", timestamp);
        params.put("keyInfo", generateKeyInfo(timestamp));
        params.put("platformCode", platformCode);
        log.info("getAccessToken params => {}", JSON.toJSONString(params));

        JSONObject json = doPost("/apigateway/getAccessToken", Json.toJson(params));
        JSONObject tokenInfoJson = json.getJSONObject("tokenInfo");
        if (tokenInfoJson != null) {
            TyTokenInfoDTO tokenInfo = tokenInfoJson.toJavaObject(TyTokenInfoDTO.class);
            return tokenInfo.getAccessToken();
        }

        throw new RuntimeException("tokenInfo为空");
    }

    public List<TyOrgUserDTO> getOrgStudentList(String orgId, int start, int end) {
        return getOrgUserList(orgId, "0", start, end);
    }

    public List<TyOrgUserDTO> getOrgTeacherList(String orgId, int start, int end) {
        return getOrgUserList(orgId, "1", start, end);
    }

    /**
     * @param orgId    学校id
     * @param userType 用户类别0:学生1:老师2:家长3:机构4:学校5:学校工作人员 6:机构工作人员
     * @param start    从0开始
     * @param end      结束位置
     * @return
     */
    public List<TyOrgUserDTO> getOrgUserList(String orgId, String userType, int start, int end) {
        String path = "/baseInfo/user/getUserList?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("orgId", orgId);
        params.put("userType", userType);
        params.put("needType", "family");
        params.put("start", String.valueOf(start));
        params.put("end", String.valueOf(end));
        JSONObject jsonResp = doPost(path, Json.toJson(params));

        List<TyOrgUserDTO> result = new ArrayList<>();
        JSONArray userList = jsonResp.getJSONArray("userList");
        for (int i = 0; i < userList.size(); i++) {
            JSONObject userObject = userList.getJSONObject(i);
            TyOrgUserDTO user = userObject.toJavaObject(TyOrgUserDTO.class);

            // 解析头像
            JSONArray logoList = userObject.getJSONArray("logoList");
            if (logoList != null && logoList.size() > 0) {
                JSONObject logObject = logoList.getJSONObject(0);
                String logoUrl = logObject.getString("logoUrl");
                user.setAvatar(logoUrl);
            }

            result.add(user);
        }

        return result;
    }

    /**
     * 获取机构下所有成员列表，包括学生和教师，返回信息中包含了
     *
     * @param orgId
     * @return
     */
    public List<TyOrgMemberDTO> getAllOrgMetemberList(String orgId) {
        int page = 1, pageSize = 100;
        List<TyOrgMemberDTO> result = new ArrayList<>();
        while (true) {
            List<TyOrgMemberDTO> list = getOrgMemberList(orgId, page, pageSize);
            if (list.isEmpty()) {
                break;
            }
            result.addAll(list);
            page++;
        }

        return result;
    }

    /**
     * 查询节点下的成员列表
     * 文档地址：https://www.showdoc.com.cn/2026844702572217/9158748760200110
     * @param orgId
     * @param page
     * @param pageSize
     * @return 成员信息
     */
    public List<TyOrgMemberDTO> getOrgMemberList(String orgId, int page, int pageSize) {
        String path = "/user/openapi/user/get-org-member-simple-list?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("org_id", orgId);        // 机构id
        params.put("org_recursion", "1");   // 是否包含下级节点成员，0:不包含（默认），1:包含所有下级
        params.put("user_type", "0,1");     // 用户类型，学生:0 教师:1
        params.put("need_type", "edu");     // dep部门节点,edu班级节点,family学生家长数据,多个使用,逗号分隔
        params.put("page", String.valueOf(page));
        params.put("page_size", String.valueOf(pageSize));
        JSONObject jsonResp = doGet(path, params);
        JSONObject result = jsonResp.getJSONObject("result");
        if (result.getInteger("count") == 0) {
            return new ArrayList<>();
        }
        JSONArray memberList = result.getJSONArray("member_list");

        List<TyOrgMemberDTO> dtoList = new ArrayList<>();
        for (int i = 0; i < memberList.size(); i++) {
            JSONObject obj = memberList.getJSONObject(i);
            TyOrgMemberDTO member = new TyOrgMemberDTO();
            member.setName(obj.getString("name"));
            member.setUserId(obj.getString("user_id"));
            member.setUserType(obj.getInteger("user_type"));

            // 班级列表
            if (obj.containsKey("user_org")) {
                List<String> classList = new ArrayList<>();
                JSONArray orgArr = obj.getJSONArray("user_org");
                for (int j = 0; j < orgArr.size(); j++) {
                    JSONObject orgObj = orgArr.getJSONObject(j);
                    String id = orgObj.getString("org_id");
                    String type = orgObj.getString("org_type");
                    if ("7".equals(type)) { // 班级类型
                        classList.add(id);
                    }
                }
                member.setClassList(classList);
            }

            // 家庭成员
            if (obj.containsKey("user_family")) {
                List<TyFamilyDTO> familyList = new ArrayList<>();
                JSONArray familyArr = obj.getJSONArray("user_family");
                for (int k = 0; k < familyArr.size(); k++) {
                    JSONObject familyObj = familyArr.getJSONObject(k);
                    TyFamilyDTO familyDTO = new TyFamilyDTO();
                    familyDTO.setFamilyId(familyObj.getString("family_id"));
                    familyDTO.setRelName(StrUtil.subAfter(familyObj.getString("family_name"), "的", true));
                    familyDTO.setUserId(member.getUserId());
                    familyList.add(familyDTO);
                }
                member.setFamilyList(familyList);
            }

            dtoList.add(member);
        }

        return dtoList;
    }

    public List<TyUserFaceDTO> syncFaceInfo(String orgId, Date startTime) {
        String path = "http://d00a82bc93774828edd6280dcea6e7ed.ty-gateway.huijiaoyun.com/app-iot/remote/api/facelib/syncFaceInfo";
        Map<String, String> params = new HashMap<>();
        params.put("orgId", orgId);
        if (startTime == null) {
            params.put("startTime", "0");
        } else {
            params.put("startTime", URLEncodeUtil.encode(Times.sDT(startTime)));
        }
        params.put("needFeatureCode", "false");
        JSONObject jsonResp = doGet(path, params);
        JSONArray data = jsonResp.getJSONArray("data");
        return data.toJavaList(TyUserFaceDTO.class);
    }

    public List<String> getTeaching(String teacherUserId) {
        String path = "/baseInfo/user/getTeachingInfo?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("personId", teacherUserId);
        JSONObject jsonResp = doPost(path, Json.toJson(params));

        List<String> classIdList = new ArrayList<>();
        JSONArray teachList = jsonResp.getJSONArray("teachList");
        for (int i = 0; i < teachList.size(); i++) {
            JSONObject teachObject = teachList.getJSONObject(i);
            String classId = teachObject.getString("classId");
            classIdList.add(classId);
        }
        return classIdList;
    }

    public Object getUserInfo(String userId) {
        String path = "/user/openapi/user/get-user-info?&accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("user_id", userId);
        JSONObject jsonResp = doGet(path, params);
        JSONObject result = jsonResp.getJSONObject("result");
        return result;
    }

    /**
     * 获取班级列表
     *
     * @param orgId 学校id
     * @return [{classId, className, gradeClass}]
     */
    public List<TyClassDTO> getClassList(String orgId, String classId) {
        String path = "/baseInfo/class/queryClassList?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("orgId", orgId);
        params.put("start", "0");
        params.put("end", "500");
        if (classId != null) {
            params.put("classId", classId);
        }
        JSONObject jsonResp = doPost(path, JSON.toJSONString(params));

        JSONArray classList = jsonResp.getJSONArray("classList");
        return classList.toJavaList(TyClassDTO.class);
    }

    public void getOrgList(String orgId) {
        String path = "/baseInfo/org/getOrgList?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("orgId", orgId);
        params.put("type", "1");
        params.put("start", "0");
        params.put("end", "100");
        JSONObject jsonResp = doPost(path, JSON.toJSONString(params));

    }

    public List<TyOrg> getEduChild(String orgId) {
        String path = "/user/openapi/org/get-edu-child-list?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("org_id", orgId);
//        params.put("school_id", "8005cf063fe811eea6a1020315160be5");
//        params.put("org_type", "3");
        JSONObject jsonResp = doGet(path, params);
        JSONObject result = jsonResp.getJSONObject("result");
        if (result.getInteger("count") == 0) {
            return new ArrayList<>();
        }

        List<TyOrg> orgList = new ArrayList<>();
        JSONArray list = result.getJSONArray("list");
        for (int i = 0; i < list.size(); i++) {
            JSONObject obj = list.getJSONObject(i);
            TyOrg org = new TyOrg();
            org.setId(obj.getString("org_id"));
            org.setOrgName(obj.getString("org_name"));
            org.setOrgType(obj.getInteger("org_type"));
            org.setParentId(obj.getString("parent_id"));
            org.setDisplayOrder(obj.getString("display_order"));
            org.setGmtCreate(new Date());
            org.setGmtModified(new Date());

            orgList.add(org);
        }
        return orgList;
    }

    public void getOrgInfo(String orgId) {
        String path = "/baseInfo/org/getOrgInfo?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("orgId", orgId);
        JSONObject jsonResp = doPost(path, JSON.toJSONString(params));
    }

    /**
     * 获取班级成员，包括学生和老师
     *
     * @param orgId   学校id
     * @param classId 班级id
     * @return [{user_id, name, gender, user_type, avatar_list}]
     * user_type 用户类型（0学生，1老师）
     */
    public List<TyClassMemberDTO> getClassSMemberList(String orgId, String classId) {
        String path = "/user/openapi/org/get-class-member-list?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("top_org_id", orgId);
        params.put("org_id", classId);
        params.put("page", "1");
        params.put("page_size", "100");
        JSONObject jsonResp = doGet(path, params);
        jsonResp = jsonResp.getJSONObject("result");

        List<TyClassMemberDTO> result = new ArrayList<>();
        JSONArray list = jsonResp.getJSONArray("member_list");
        for (int i = 0; i < list.size(); i++) {
            JSONObject obj = list.getJSONObject(i);
            TyClassMemberDTO member = new TyClassMemberDTO();
            member.setUserId(obj.getString("user_id"));
            member.setName(obj.getString("name"));
            member.setGender(obj.getString("gender"));
            member.setUserType(obj.getString("user_type"));
            JSONObject avatarList = obj.getJSONObject("avatar_list");
            String avatar = avatarList.getString("1");
            if (Strings.isNotBlank(avatar)) {
                member.setAvatar(avatar);
            }

            result.add(member);
        }
        return result;
    }

    public String getAccountInfo(String userId) {
        String path = "/baseInfo/user/privacy/account?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("user_id", userId);

        JSONObject jsonResp = doPost(path, Json.toJson(params));
        String text = jsonResp.getString("result");
        return decrypt(text);
    }

    public String getMobile(String userId) {
        String path = "/baseInfo/user/privacy/phone?accessToken=" + getAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put("user_id", userId);

        JSONObject jsonResp = doPost(path, Json.toJson(params));
        String text = jsonResp.getString("result");
        if (Strings.isBlank(text)) {
            return null;
        }
        return decrypt(text);
    }

    String decrypt(String text) {
        return getAesCipher().decryptStr(text);
    }

    Map<String, AES> aesMap = new HashMap<>();

    AES getAesCipher() {
        String day = Times.format("yyyyMMdd", new Date());
        if (!aesMap.containsKey(day)) {
            String key = Lang.md5(appId + "fH?X5MeZWGYTK1KZ" + day);
            key = StrUtil.sub(key, 8, 24).toLowerCase();  // 16位小写字符
            AES aes = SecureUtil.aes(key.getBytes());

            aesMap.put(day, aes);
        }
        return aesMap.get(day);
    }

    private String generateKeyInfo(String timestamp) {
        String keyInfo = appId + appKey + timestamp;
        byte[] hmacSHA = EncryptionUtils.getHmacSHA1(keyInfo, appKey);
        String digest = EncryptionUtils.bytesToHexString(hmacSHA);
        if (Strings.isNotBlank(digest)) {
            digest = digest.toUpperCase();
        }
        return digest;
    }

    JSONObject doPost(String path, String requestBody) {
        log.info("doPost path => {}, requestBody => {}", path, requestBody);
//        String resp = myHttpClient.doPost(StrUtil.removeSuffix(baseUrl, "/") + path, requestBody);

        Header header = Header.create().set("Content-Type", "application/json");
        Response response = Http.post3(StrUtil.removeSuffix(baseUrl, "/") + path, requestBody, header, 6000);
        String resp = response.getContent();
        log.info("response : {}", resp);
        JSONObject json = JSONObject.parseObject(resp);

        checkResponse(json);
        return json;
    }

    JSONObject doGet(String path, Map<String, String> params) {
        if (!path.contains("?")) {
            path = path + "?";
        } else {
            path = path + "&";
        }

        // 拼接query参数
        for (String key : params.keySet()) {
            String value = params.get(key);
            path = path + key + "=" + value + "&";
        }
        path = StrUtil.removeSuffix(path, "&");

        log.info("doGet path => {}", path);

        // 拼接host
        String host = StrUtil.removeSuffix(baseUrl, "/");
        if (path.startsWith("http")) {
            host = "";
        }

        // headers，人脸同步接口使用的是网关鉴权，需要在header中添加鉴权参数
        Map<String, String> headers = new HashMap<>();
        if (path.contains("d00a82bc93774828edd6280dcea6e7ed")) {
            headers = buildUniAuthHeaders();
        }
        Response response = Http.get(host + path, Header.create(headers), 6000);

//        String resp = myHttpClient.doGet(host + path, headers);
        String resp = response.getContent();
        log.info("response : {}", resp);
        JSONObject json = JSONObject.parseObject(resp);

        checkResponse(json);
        return json;
    }

    private final Map<String, HMac> hMacMap = new HashMap<>();

    Map<String, String> buildUniAuthHeaders() {
        Map<String, String> headers = new HashMap<>();
        String now = System.currentTimeMillis() + "";

        headers.put("TY-Stage", "1");
        headers.put("TY-AppKey", uniAppId);
        headers.put("TY-Timestamp", now);

        // HmacSHA256
        HMac mac = hMacMap.get(uniAppSecret);
        if (mac == null) {
            mac = SecureUtil.hmacSha256(Base64.decode(uniAppSecret));
            hMacMap.put(uniAppSecret, mac);
        }
        String content = uniAppId + "&" + "GET" + "&1&" + now;
        String signature = mac.digestBase64(content, false);

        headers.put("TY-Signature", signature);
        return headers;
    }

    void checkResponse(JSONObject resp) {
        if ("000000".equals(resp.getString("retCode")) ||
                "000000".equals(resp.getString("code")) ||
                "0".equals(resp.getString("code"))) {
            return;
        }

        String message = resp.getString("retDesc");
        if (Strings.isBlank(message)) {
            message = resp.getString("message");
        }
        if (Strings.isBlank(message)) {
            message = resp.getString("msg");
        }

        throw new RuntimeException("接口调用失败 : " + message);
    }
}
